package com.quectel.taskserver.job.event;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.func.LambdaUtil;
import com.google.common.base.Preconditions;
import com.quectel.business.dms.AccessorUtils;
import com.quectel.business.dms.application.ApplicationAccessConf;
import com.quectel.business.dms.application.access.accessor.im.AbstractIntelligentMonitorAccessor;
import com.quectel.constant.core.device.DeviceConstants;
import com.quectel.constant.core.device.DeviceTypeCodeConstants;
import com.quectel.constant.core.eventrecord.AiEventRecordConstants;
import com.quectel.constant.global.SystemConstants;
import com.quectel.core.module.ai.dto.mongo.MongoAiEventRecordDto;
import com.quectel.core.module.application.dto.ApplicationDto;
import com.quectel.core.module.application.service.ApplicationService;
import com.quectel.core.module.device.dto.DeviceDto;
import com.quectel.core.module.device.service.DeviceService;
import com.quectel.sdk.dahua9100.Dahua9100ClientBuilder;
import com.quectel.sdk.dahua9100.monitor.MonitorClient;
import com.quectel.sdk.dahua9100.monitor.model.SnapFaceRecordResp;
import com.quectel.util.common.DateUtils;
import com.quectel.util.common.JacksonUtils;
import com.quectel.util.common.MyFileUtils;
import com.quectel.util.kit.StorageKit;
import com.quectel.util.redis.RedisUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.shardingsphere.elasticjob.api.ShardingContext;
import org.apache.shardingsphere.elasticjob.dataflow.job.DataflowJob;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.io.File;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 大华9100监控 人脸抓拍记录定时抓取任务
 *
 * @author: rananxin
 * @email: ryan.ran@quectel.com
 * @date: 2022-11-09 16:16:00
 */
@Component
public class Dahua9100EventFetchTask implements DataflowJob<ApplicationDto> {

    @DubboReference
    private ApplicationService applicationService;
    @DubboReference
    private DeviceService deviceService;
    @DubboReference

    private static final Logger LOGGER = LoggerFactory.getLogger(Dahua9100EventFetchTask.class);

    private static final String DAHUA_9100_FETCH_EVENT_EXEC_LIMIT = "dahua_9100_fetch_event_exec_limit:";
    private static final String DAHUA_9100_FETCH_FACE_RECORD_LAST_TIME = "dahua_9100_fetch_face_record_last_time:";

    @Override
    public List<ApplicationDto> fetchData(ShardingContext shardingContext) {
        List<ApplicationDto> applicationDtos = applicationService
                .fetchByConfCode(ApplicationAccessConf.ConfCodeEnum.DAHUA_9100.getCode()
                        , shardingContext.getShardingTotalCount()
                        , shardingContext.getShardingItem());
        return applicationDtos.stream().filter(o -> DeviceTypeCodeConstants.DAHUA_9100_MONITOR.equals(o.getDeviceType().getCode())).collect(Collectors.toList());
    }

    @Override
    public void processData(ShardingContext shardingContext, List<ApplicationDto> list) {
        boolean setnx = RedisUtils.setnx(DAHUA_9100_FETCH_EVENT_EXEC_LIMIT + shardingContext.getShardingItem(), "", 30, TimeUnit.MINUTES);
        if (setnx) {
            final Date currentDate = new Date();
            try {
                for (ApplicationDto application : list) {
                    try {
                        ApplicationAccessConf.TemplateOfDahua9100Conf templateOfDahua9100Conf = ApplicationAccessConf
                                .parseExtStr(ApplicationAccessConf.TemplateOfDahua9100Conf.class, application.getAccessConf());

                        assert templateOfDahua9100Conf != null;
                        MonitorClient build = Dahua9100ClientBuilder.builder()
                                .username(templateOfDahua9100Conf.getUsername())
                                .password(templateOfDahua9100Conf.getPassword())
                                .ip(templateOfDahua9100Conf.getIp())
                                .build(MonitorClient.class);

                        Date lastDate = null;
                        String lastDateStr = RedisUtils.get(DAHUA_9100_FETCH_FACE_RECORD_LAST_TIME + application.getId());
                        if (StringUtils.isBlank(lastDateStr)
                                || DateUtil.between(DateUtils.parse(lastDateStr, DateUtils.FormatType.COMMON), currentDate, DateUnit.HOUR, false) > 6) {
                            // 没找到上一次任务执行时间，或者上一次任务执行时间大于6小时，从6小时前获取
                            lastDate = DateUtil.offsetHour(currentDate, -6);
                        } else {
                            lastDate = DateUtils.parse(lastDateStr, DateUtils.FormatType.COMMON);
                        }

                        // 准确查找设备 该应用下同设备类型非游离设备
                        Map<String, Object> seekFitDeviceParams = new HashMap<>();
                        seekFitDeviceParams.put(LambdaUtil.getFieldName(DeviceDto::getApplication), application.getId());
                        seekFitDeviceParams.put(DeviceConstants.QueryParamEnum.NOT_FREE.getParamKey(), SystemConstants.NORMAL.toString());
                        seekFitDeviceParams.put(LambdaUtil.getFieldName(DeviceDto::getDeviceTypeId), application.getDeviceTypeId());
                        List<DeviceDto> deviceDtoList = deviceService.queryList(seekFitDeviceParams);
                        build.fetchFaceRecord(deviceDtoList.stream()
                                        .map(DeviceDto::getUniqueId)
                                        .collect(Collectors.toList())
                                , lastDate, currentDate, this::recordHand);
                        RedisUtils.set(DAHUA_9100_FETCH_FACE_RECORD_LAST_TIME + application.getId()
                                , DateUtils.format(currentDate, DateUtils.FormatType.COMMON)
                                , 15, TimeUnit.DAYS);
                    } catch (Exception e) {
                        LOGGER.error("大华9100查询人脸抓拍异常", e);
                    }
                }
            } finally {
                RedisUtils.del(DAHUA_9100_FETCH_EVENT_EXEC_LIMIT + shardingContext.getShardingItem());
            }
        }
    }

    private void recordHand(List<SnapFaceRecordResp.ResultsCls> resultsList) {
        if (CollectionUtils.isEmpty(resultsList)) {
            return;
        }

        for (SnapFaceRecordResp.ResultsCls resultsCls : resultsList) {
            List<DeviceDto> deviceDtos = deviceService.selectCacheByUniqueId(resultsCls.getChannelId());
            if (CollUtil.isEmpty(deviceDtos)) {
                continue;
            }

            File file = MyFileUtils.downloadToTempDir(resultsCls.getFaceImgUrl(), false);
            String url = StorageKit.uploadToPublicBucketObtainUrl(file);

            for (DeviceDto deviceDto : deviceDtos) {
                if (deviceDto.getVillageId() == null) {
                    continue;
                }
                MongoAiEventRecordDto aiEventRecordDto = new MongoAiEventRecordDto();
                aiEventRecordDto.setTenantId(deviceDto.getTenantId());
                aiEventRecordDto.setVillageId(deviceDto.getVillageId());
                aiEventRecordDto.setBuildingId(deviceDto.getBuildingId());
                aiEventRecordDto.setDeviceCategoryId(deviceDto.getDeviceCategoryId());
                aiEventRecordDto.setDeviceTypeId(deviceDto.getDeviceTypeId());
                aiEventRecordDto.setDeviceId(deviceDto.getId());
                aiEventRecordDto.setDeviceName(deviceDto.getName());
                aiEventRecordDto.setEventType(AiEventRecordConstants.EventTypeEnum.PERSON_FACE_CAPTURE.getEventType());
                aiEventRecordDto.setSnapImages(JacksonUtils.toJsonString(Collections.singletonList(url)));
                aiEventRecordDto.setEventTime(resultsCls.getCapTime());
                aiEventRecordDto.setLatitude(deviceDto.getLatitude());
                aiEventRecordDto.setLongitude(deviceDto.getLongitude());
                AbstractIntelligentMonitorAccessor intelligentMonitorAccessor = AccessorUtils.getIntelligentMonitorAccessor(deviceDto.getDeviceType());
                Preconditions.checkNotNull(intelligentMonitorAccessor);
                intelligentMonitorAccessor.aiEventHandler(aiEventRecordDto);
            }
        }
    }
}
